
/**
	This file is part of MemoryTrace.

	MemoryTrace is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

        Contributed by Ameziane NAIT ABDELAZIZ.
        
	MemoryTrace is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with MemoryTrace.  If not, see <http://www.gnu.org/licenses/>.
**/

/*! 
  \file MESSAGE_QUEUE.h
  \brief Une classe template permettant de construire des files de messages pour faire de l'ipc.
  \author Ameziane NAIT ABDELAZIZ
*/



#ifndef MESSAGE_QUEUE_H
#define	MESSAGE_QUEUE_H

#include <cassert>
#include <sys/msg.h>
#include <stddef.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <errno.h>

/*-------------------MESSAGE QUEUES FUNCTIONS-------------------------*/

#define FLAG 0
#define SLEEP sleep(2)

/**
 * \brief message tag pour échanger des messages entre deux processus
 */
enum MESSAGE_TAG{
  MSG_TAG_ALL=0,
  MSG_TAG_RECU=1,
  MSG_TAG_QUIT=2,
  MSG_TAG_PLEIN=3,
};


/**
 * \brief identifiants de file de message
 */
enum QUEUES_ID{
  QID_1=0,
  QID_2=1,
  QID_3=2,
  QID_4=3,
};


/**
 * \brief structure de données message
 */
typedef struct {
  /**
   * identifiant du message , equivalent MPI du message tag
   */
  long mtype;
  
  /**
   * contenu du message
   */
  long events_count;
} message_t;


/**
 * \class MESSAGE_QUEUE
 * \brief Cette classe permet d'envoyer des messages entre processus
 */
template <QUEUES_ID queueID> class MESSAGE_QUEUE{
  
 private:
  /**
   * \brief clef 1 pour file de message numéro 1
   */
  static key_t key_12;
  
  /**
   * \brief identifiant de la file de message sens 1->2
   */
  static int msqid_12;
  
  /**
   * \brief clef 2 pour file de message numéro 2
   */
  static key_t key_21;
  
  /**
   * \brief identifiant de la file de message sens 2->1 
   */
  static int msqid_21; 
  
  /**
   * \brief instance de message que s'echangent les processus 
   */
  static message_t msg;
  
 private:
  /**
   * \brief génére une clef pour une message queue à partir d'un fichier, producteur 
   */
  static void makeProducerKey(const char* path);
  /**
   * \brief génére une clef pour une message queue à partir d'un fichier, consommateur
   * @param path
   */
  static void makeConsumerKey(const char* path);
    
public:    
    /**
     * \brief envoie un message au producteur
     * @param tag
     * @return 
     */
    static int  sendMessageToProducer(MESSAGE_TAG tag);
    
    /**
     *\brief reçoit un message du consommateur 
     * @param msg
     * @param tag
     * @return 
     */
    static long  receiveMessageFromConsumer(MESSAGE_TAG tag);
    
    /**
     * \brief envoie un message au consommateur
     * @param tag
     * @return 
     */
    static int  sendMessageToConsumer(MESSAGE_TAG tag,long events_count);
    
    
    /**
     *\brief retourne le nombre d'événement d'allocations
     * @return 
     */
    static long getEventsCount();
    
    /**
     * \brief reçoit un message du producteur
     * @param msg
     * @param tag
     * @return 
     */
    static long  receiveMessageFromProducer(MESSAGE_TAG tag);
    
    
    /**
     * \brief ferme les files d'attentes
     */
    static void destroyQueues();
    
    /**
     *\brief initialise les files d'attentes 
     */
    static void initQueues();
};



/*----------IMPELMENTATION DES MESSAGE QUEUES-------------------------*/
template <QUEUES_ID queueID> key_t       MESSAGE_QUEUE<queueID>::key_12;
template <QUEUES_ID queueID> int         MESSAGE_QUEUE<queueID>::msqid_12;
template <QUEUES_ID queueID> key_t       MESSAGE_QUEUE<queueID>::key_21;
template <QUEUES_ID queueID> int         MESSAGE_QUEUE<queueID>::msqid_21; 
template <QUEUES_ID queueID> message_t   MESSAGE_QUEUE<queueID>::msg;



template <QUEUES_ID queueID> void  MESSAGE_QUEUE<queueID>::makeProducerKey(const char* path){
  assert((key_12 = ftok(path,'B'))!=-1);
  assert((msqid_12 = msgget(key_12,0644 | IPC_CREAT))!=-1);
}

template <QUEUES_ID queueID> void MESSAGE_QUEUE<queueID>::makeConsumerKey(const char* path){
  assert((key_21 = ftok(path,'B'))!=-1);
  assert((msqid_21 = msgget(key_21,0644 | IPC_CREAT))!=-1);
}


template <QUEUES_ID queueID> int  MESSAGE_QUEUE<queueID>::sendMessageToProducer(MESSAGE_TAG tag){
  msg.mtype=tag;
  if(msgsnd(msqid_21,&msg,sizeof(message_t)-sizeof(long),FLAG)==-1){
    SLEEP;
    printf("echec de l'envoie erreur : %d =>msg type :%li\n",errno,msg.mtype);
    return -1;
  };
  
  return 0;
}

template <QUEUES_ID queueID> long  MESSAGE_QUEUE<queueID>::receiveMessageFromConsumer(MESSAGE_TAG tag){
  if(msgrcv(msqid_21,&msg,sizeof(message_t)-sizeof(long),tag,FLAG)==-1){
    SLEEP;
    printf("echec de la reception : %d msg type :%ld\n",errno,msg.mtype);
    return -1;
  };
  return msg.mtype;
}

template <QUEUES_ID queueID> int  MESSAGE_QUEUE<queueID>::sendMessageToConsumer(MESSAGE_TAG tag,long events_count){
  
  msg.mtype=tag;
  msg.events_count=events_count;
  if(msgsnd(msqid_12,&msg,sizeof(message_t)-sizeof(long),FLAG)==-1){
    return -1;
  } ;
  
  return 0;
}



template <QUEUES_ID queueID> long  MESSAGE_QUEUE<queueID>::receiveMessageFromProducer(MESSAGE_TAG tag){
  if(msgrcv(msqid_12,&msg,sizeof(message_t)-sizeof(long),tag,FLAG)==-1){
    //printf("echec de la reception\n");
  };
  //printf("recu du producteur %d evenements\n",msg.events_count);
  return msg.mtype;
}

template <QUEUES_ID queueID> void MESSAGE_QUEUE<queueID>::destroyQueues(){
  //printf("fin des files\n");
  assert(msgctl(msqid_12,IPC_RMID,NULL)!=-1);
  assert(msgctl(msqid_21,IPC_RMID,NULL)!=-1);
};


template <QUEUES_ID queueID> void MESSAGE_QUEUE<queueID>::initQueues(){
  makeConsumerKey("../");
  makeProducerKey(".");
}


template <QUEUES_ID queueID> long MESSAGE_QUEUE<queueID>::getEventsCount(){ 
  return msg.events_count;
}
#endif	/* MESSAGE_QUEUES_H */

